<?php
/**
 * upload.libaray.php
 * @Copyright lemonCarlJ All rights reserved.
 * @License http://www.56code.com/License.txt
 * @Version 1.0.0
 * @Email rpgshifa@aliyun.com
 */
namespace SF\libaray;

class upload
{
    /**
     * 最大上传尺寸
     * @var int
     */
    public $max_size = 0;
    
    /**
     * 允许上传文件后缀
     * @var null|array
     */
    public $allow_extension = null;
    
    /**
     * 允许上传类型
     * @var null|array
     */
    public $allow_type = null;
    
    /**
     * 是否允许覆盖
     * @var boolean
     */
    public $replace = false;
    
    /**
     * 错误id
     * @var null|int|array
     */
    protected $error_no = null;
    
    /**
     * 错误信息
     * @var null|string|array
     */
    protected $error_msg = null;
    
    /**
     * 单文件上传
     * @param string $file 上传文件信息
     * @param string $directory 上传目录
     * @param string $prefix 文件名前缀 [ null ]
     * @return boolean 返回是否上传成功
     */
    public function file( $file, $directory, $prefix = null )
    {
        //初始化上传信息
        $this->initial_upload();
        
        //系统级错误
        if( $file[ 'error' ] )
        {
            $this->system_error( $file[ 'error' ] );
            return false;
        }
        
        //未上传文件
        if( empty( $file[ 'name' ] ) )
        {
            $this->system_error( 4 );
            return false;
        }
        
        //保存目录是否存在
        if( ! file_exists( $directory ) )
        {
            $this->error_no = -1;
            $this->error_msg = '找不到上传目录[ ' . $directory . ' ]';
            return false;
        }
        
        if ( !is_writeable( $directory ) ) {
            $this->error_no = -2;
            $this->error_msg = '没有上传目录的写入权限[ ' . $directory . ' ]';
            return false;
        }
        
        //上传文件
        $file_name = $file[ 'name' ];
        $save_file = realpath( $directory ) . DIRECTORY_SEPARATOR . $prefix . $file_name;
        
        //相同文件是否覆盖
        if( file_exists( $save_file ) && ! $this->replace )
        {
            $this->error_no = -3;
            $this->error_msg = '已存在相同文件,设置不允许覆盖';
            return false;
        }
        
        //检测允许上传类型
        if( is_array( $this->allow_type ) )
        {
            if( ! in_array( $file[ 'type' ], $this->allow_type ) )
            {
                $this->error_no = -4;
                $this->error_msg = '禁止上传的文件类型[ ' . $file[ 'type' ] . ' ]';
                return false;
            }
        }
        
        //检测允许上传后缀
        if( is_array( $this->allow_extension ) )
        {
            if( ! $this->verify_extension( $file_name ) )
            {
                $this->error_no = -5;
                $this->error_msg = '禁止上传的文件后缀[ ' . $this->extension( $file_name ) . ' ]';
                return false;
            }
        }
        
        //检测允许上传文件大小
        if( $this->max_size && $this->max_size < $file[ 'size' ] )
        {
            $this->error_no = -6;
            $this->error_msg = '上传文件超过限制尺寸';
            return false;
        }
        
        //检测文件是否为上传文件
        if( ! is_uploaded_file( $file[ 'tmp_name' ] ) )
        {
            $this->error_no = -999;
            $this->error_msg = '非法上传文件！';
            return false;
        }
        
        if( ! move_uploaded_file( $file[ 'tmp_name' ], $save_file ) )
        {
            $this->error_no = -7;
            $this->error_msg = '文件上传移动失败！';
            return false;
        }
        
        $this->error_no = 0;
        $this->error_msg = '文件上传成功！';
        return true;
    }
    
    /**
     * 多文件上传
     * @param string $files_info 文件上传信息列表
     * @param string $directory 上传目录
     * @param string $prefix 文件名前缀 [ null ]
     * @param array 返回一组上传信息
     */
    public function files( $files_info, $directory, $prefix = null )
    {
        //初始化上传信息
        $this->initial_upload();
        
        //上传文件列表
        $files = array();
        
        //组合信息
        foreach( $files_info as $key => $values )
        {
            $files = $this->serialize_file_list( $files, $key, $values );
        }
        
        //批量上传文件
        $result = $this->upload_files( $files, $directory, $prefix );
        
        //全局错误信息
        $this->error_no = $result[ 'error_no' ];
        $this->error_msg = $result[ 'error_msg' ];
        
        return $result;
    }
    
    /**
     * 多文件上传操作
     * @param array $files 上传文件列表
     * @param string $directory 上传目录
     * @param string $prefix 文件名前缀
     * @return array 返回上传信息列表
     */
    protected function upload_files( $files, $directory, $prefix )
    {
        //错误id集
        $error_no = array();
        
        //错误信息集
        $error_msg = array();
        
        foreach ( $files as $key => $file )
        {
            if( is_array( current( $file ) ) )
            {
                $result = $this->upload_files( $file, $directory, $prefix );
                
                //记录信息
                $error_no[ $key ] = $result[ 'error_no' ];
                $error_msg[ $key ] = $result[ 'error_msg' ];
            }else{
                $this->file( $file, $directory, $prefix );
                
                //记录信息
                $error_no[ $key ] = $this->error_no;
                $error_msg[ $key ] = $this->error_msg;
            }
        }
        
        return array(
            'error_no' => $error_no,
            'error_msg' => $error_msg
        );
    }
    
    /**
     * 序列化文件列表
     * @param array $collection 属性集合
     * @param string $name 增加属性名
     * @param array $property 增加属性
     */
    protected function serialize_file_list( $collection, $name, $property )
    {
        foreach( $property as $key => $value )
        {
            if( ! isset( $collection[ $key ] ) )
            {
                $collection[ $key ] = array();
            }
            
            if( is_array( $value ) )
            {
                $collection[ $key ] = $this->serialize_file_list( $collection[ $key ], $name, $value );
            }else{
                $collection[ $key ][ $name ] = $value;
            }
        }
        
        return $collection;
    }
    
    /**
     * 初始化上传
     */
    protected function initial_upload()
    {
        $this->error_no = null;
        
        $this->error_msg = null;
    }
    
    /**
     * 系统错误信息
     * @param string $error_no 错误号
     */
    protected function system_error( $error_no )
    {
        $this->error_no = $error_no;
        switch ( $error_no )
        {
            case 1 :
                $this->error_msg = '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。';
                break;
            case 2 :
                $this->error_msg = '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。';
                break;
            case 3 :
                $this->error_msg = '文件只有部分被上传。';
                break;
            case 4 :
                $this->error_msg = '没有文件被上传。';
                break;
            case 6 :
                $this->error_msg = '找不到临时文件夹。';
                break;
            case 7 :
                $this->error_msg = '文件写入失败。';
                break;
            case 8 :
                $this->error_msg = 'PHP扩展停止了文件上传。';
                break;
            default :
                $this->error_msg = '未知错误';
                break;
        }
    }
    
    /**
     * 验证文件后缀
     * @param string $file_name 文件名
     * @return boolean 返回是否在验证列表
     */
    protected function verify_extension( $file_name )
    {
        $extension = $this->extension( $file_name );
        return in_array( $extension, $this->allow_extension );
    }
    
    /**
     * 获取文件后缀
     * @param string $file_name 文件名
     * @return string 返回文件后缀名
     */
    protected function extension( $file_name )
    {
        $path_info = pathinfo( $file_name );
        return isset( $path_info[ 'extension' ] ) ? $path_info[ 'extension' ] : '';
    }
    
    public function __get( $name )
    {
        return $this->$name;
    }
}